/*
 * Copyright 2022 The Open Islands Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.openislands.oi.master.scheduler;

import org.openislands.oi.constant.MessageCodeEnum;
import org.openislands.oi.constant.NodeState;
import org.openislands.oi.error.LogicException;
import org.openislands.oi.manager.CommonTransactionManager;
import org.openislands.oi.master.manager.MasterNodeManager;
import org.openislands.oi.pojo.dto.CommonResult;
import org.openislands.oi.pojo.dto.NodeDTO;
import org.openislands.oi.pojo.vo.CommonResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;

import javax.annotation.Resource;
import java.util.List;

@Configuration
@SuppressWarnings("unused")
public class NodeMonitor {
    private static final Logger log = LoggerFactory.getLogger(NodeMonitor.class);

    @Resource(type = MasterNodeManager.class)
    private MasterNodeManager nodeManager;

    @Resource(type = NodeCoordinator.class)
    private NodeCoordinator nodeCoordinator;

    @Resource
    private CommonTransactionManager commonTransactionManager;

    public void nodeVerify() {
        log.debug("------node verify------start");
        // unverified node
        final NodeDTO queryDTO = new NodeDTO();
        queryDTO.setEnabled(true);
        queryDTO.setState(NodeState.UNVERIFIED);
        final List<NodeDTO> nodeDTOList = nodeManager.queryAnyListDTO(queryDTO);
        for (NodeDTO nodeDTO : nodeDTOList) {
            final String nodeId = nodeDTO.getNodeId();
            try {
                commonTransactionManager.defaultTransaction(transactionStatus -> {
                    if (nodeManager.nodeVerify(nodeDTO)) {
                        CommonResult<NodeDTO> result = nodeCoordinator.verifyNode(nodeDTO);
                        if (CommonResponse.parse(result).responseSuccess()) {
                            final NodeDTO res = result.getData();
                            res.setId(nodeDTO.getId());
                            boolean bool = nodeManager.nodeVerifyFinished(res);
                            log.info("node verify success: {}, result: {}", nodeId, bool);
                        } else {
                            throw new LogicException(result.getCode(), result.getMessage());
                        }
                        return;
                    }
                    throw new LogicException(MessageCodeEnum.DATA_UPDATE_ERROR, "node verify update");
                });
            } catch (Throwable e) {
                nodeManager.updateNodeHeartbeatState(nodeDTO, NodeState.VERIFIED_FAILED, NodeState.UNVERIFIED);
                log.warn("node {} verify failed. e: {}", nodeId, e);
            }
        }

        log.debug("------node verify------end");
    }


    public void nodeHeartbeat() {
        log.debug("------node heartbeat state sync------start");
        // interacting partners
        final List<NodeDTO> nodeDTOList = nodeManager.queryHeartbeatNode();
        for (NodeDTO nodeDTO : nodeDTOList) {
            try {
                final CommonResult<Boolean> commonResult = nodeCoordinator.pingNode(nodeDTO);
                if (commonResult.isSuccess() && commonResult.getData()) {
                    boolean bool = nodeManager.updateNodeOnline(nodeDTO);
                    log.info("node {} sync state success, node enter online state {}", nodeDTO.getNodeId(), bool);
                } else {
                    throw new LogicException(commonResult.getCode(), commonResult.getMessage());
                }
            } catch (Exception e) {
                boolean bool = nodeManager.updateNodeOffline(nodeDTO);
                log.warn("node {} sync state failed, node enter offline state {}. e: {}", nodeDTO.getNodeId(), bool, e);
            }
        }

        log.debug("------node heartbeat state sync------end");
    }


    public void nodeBreak() {
        log.debug("------node break------start");
        // break partners
        final List<NodeDTO> nodeDTOList = nodeManager.queryDeleteNode();
        for (NodeDTO nodeDTO : nodeDTOList) {
            try {
                commonTransactionManager.defaultTransaction(transactionStatus -> {
                    if (nodeManager.breakNode(nodeDTO)) {
                        // request partner node break my node
                        final CommonResult<Boolean> commonResult = nodeCoordinator.breakNode(nodeDTO);
                        if (CommonResponse.parse(commonResult).responseSuccess()) {
                            nodeManager.deleteNode(nodeDTO);
                            log.info("delete break node {} done", nodeDTO.getNodeId());
                        } else {
                            log.warn("break node {} failed. result: {}", nodeDTO.getNodeId(), commonResult);
                            // throw new LogicException(MessageCodeEnum.DATA_UPDATE_ERROR, "break node failed");
                        }
                    }
                });
            } catch (Exception e) {
                log.warn("node {} break failed. e: {}", nodeDTO.getNodeId(), e);
            }
        }

        log.debug("------node break------end");
    }
}
